<template>
  <div class="pe-box">
    <div id="rootScene" ref="rootSceneRef"></div>
    <input v-show="showInput" ref="TextInput" id="TextInput" class="text-input" v-model="word" @blur="onTextInputBlur" />
    <div class="toolbar">
      <ul>
        <li @click="handleAddImage">
          选择图片
        </li>
        <li>
          <select class="select" v-model="activeColor" placeholder="选择颜色">
            <option value="0x212121" label="黑色"/>
            <option value="0xffffff" label="白色"/>
            <option value="red" label="红色"/>
            <option value="0x00E676" label="绿色"/>
            <option value="0x2196F3" label="蓝色"/>
            <option value="0xFFC107" label="黄色"/>
          </select>
        </li>
        <li>
          <select class="select" v-model="activateFilter" placeholder="滤镜" @change="changeFilter">
            <option value="reset" label="无滤镜"/>
            <option value="sepia" label="sepia"/>
            <option value="negative" label="negative"/>
            <option value="kodachrome" label="kodachrome"/>
            <option value="lsd" label="lsd"/>
            <option value="polaroid" label="polaroid"/>
            <option value="contrast" label="contrast"/>
            <option value="grayscale" label="grayscale"/>
            <option value="predator" label="predator"/>
            <option value="saturate" label="saturate"/>
            <option value="desaturate" label="desaturate"/>
          </select>
        </li>
        <li @click="() => selectAction(1)" :class="[actionType == 1?'activeColor':'']">
          <select class="select" v-model="activeRect" placeholder="形状">
            <option value="rect" label="矩形"/>
            <option value="roundRect" label="圆角矩形"/>
            <option value="circle" label="圆形"/>
            <option value="ellipse" label="椭圆"/>
          </select>
        </li>
        <li  @click="() => selectAction(7)" :class="[actionType == 7?'activeColor':'']">
          <i class="icon-font toolbar-icon" ></i>
          <span>马赛克</span>
        </li>
        <li  @click="() => selectAction(8)" :class="[actionType == 8?'activeColor':'']">
          <i class="icon-font toolbar-icon" ></i>
          <span>裁剪</span>
        </li>
        <template v-for="(item) in toolbarList">
          <li v-if="item.id != 1" @click="() => selectAction(item.id)" :class="[actionType == item.id?'activeColor':'']">
            <i class="icon-font toolbar-icon" :class="[item.icon]"></i>
            <span>{{ item.name }}</span>
          </li>
        </template>
      </ul>
    </div>
  </div>
</template>
<script setup lang="ts">
import { Application, Container, Renderer, Assets, Sprite, RenderTexture, TextureSource,Texture, Graphics, BitmapText, NoiseFilter, ColorMatrixFilter, type FederatedPointerEvent } from 'pixi.js';
import { PixelateFilter, GrayscaleFilter, KawaseBlurFilter } from 'pixi-filters'
import {Howl, Howler} from 'howler'; // esm
import clickMp3 from '@/assets/mp3/click.mp3'
// import clickMp3 from '@/assets/mp3/qt1734.mp3'
import brushSvg from '@/assets/cursor/brush.png'
import textVideo from '@/assets/video/1.mp4'
const rootSceneRef = ref<HTMLElement>() // div根节点
const application = ref<Application<Renderer>>(new Application()) // pixi根应用
const actionType = ref<number>(0) // 动作类型
let bgContainer: Container = new Container() // pixi背景图层
let drawContainer: Container = new Container() // pixi绘制图层
const activateFilter = ref<string>('reset') // 当前滤镜
let bgSprite: Sprite = new Sprite() // pixi背景图层背景精灵图
let inputText = new BitmapText() // pixi动态文字
const TextInput = ref<any>()
const word = ref<string>('')
const showInput = ref<boolean>(false)
const activeColor = ref<string>('red')
const activeRect = ref<string>('rect') // 形状 默认矩形
const imgUrl = ref<string>('https://images.pexels.com/photos/29556746/pexels-photo-29556746.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1')
const fileUrl = ref<string>(textVideo)
const toolbarList = ref<Array<any>>([
  { id: 0, name: '画笔', icon: 'icon-shiwu-huabi' },
  { id: 1, name: '圆形', icon: 'icon-yuanxing' },
  { id: 2, name: '文字', icon: 'icon-wenzi' },
  { id: 3, name: '撤回', icon: 'icon-chehuisekuai' },
  { id: 4, name: '重做', icon: 'icon-qingkong' },
  { id: 5, name: '确认', icon: 'icon-queren' },
  { id: 6, name: '关闭', icon: 'icon-guanbi' },
])
const videoRef = ref<HTMLVideoElement>(document.createElement('video'))
let mask = new Graphics() // 图形
const colorMatrixFilter = new ColorMatrixFilter()
const clickAudio = ref<any>()
// 渲染场景
async function renderScene() {
  await application.value.init({ 
    resizeTo: rootSceneRef.value, 
    antialias: true, // 抗锯齿
  });
  bgContainer.width = application.value.screen.width;
  bgContainer.height = application.value.screen.height;
  drawContainer.height = application.value.screen.height;
  drawContainer.height = application.value.screen.height;
  rootSceneRef.value?.appendChild(application.value.canvas);
  application.value.stage.addChild(bgContainer)
  bgContainer.addChild(drawContainer); // 添加绘制层
  drawContainer.addChild(bgSprite);
  renderBgSprite();
}
// 渲染背景层精灵图
async function renderBgSprite() {
  // 使用视频第一帧作为封面
  bgSprite.texture = await Assets.load(imgUrl.value);
  bgSprite.position.set(0,0)
  bgSprite.setSize(application.value.screen.width, application.value.screen.height)
  bgSprite.filters = [colorMatrixFilter]
  bgSprite.eventMode = 'static' // 设置精灵图点击模式
  mask.setSize(0,0)
  selectAction(actionType.value) // 选择工具
}
function changeFilter() {
  const oneParams = ['sepia', 'negative', 'kodachrome', 'lsd', 'polaroid']
  const noParams = ['saturate', 'desaturate', 'reset']
  colorMatrixFilter.reset()
  if(noParams.includes(activateFilter.value)) {
    (colorMatrixFilter as any)[activateFilter.value]()
  } else if(oneParams.includes(activateFilter.value)) {
    (colorMatrixFilter as any)[activateFilter.value](true)
  } else {
    (colorMatrixFilter as any)[activateFilter.value](0.5, false)
  }
}
// 激活画笔
function activateBrush() {
  let brushGraphics = new Graphics() // 画笔
  let isDraw = false;
  // 指针按钮按下
  bgSprite.on('pointerdown', (e: FederatedPointerEvent) => {
    if(isDraw) return;
    isDraw = true;
    const {x, y} = e.global
    brushGraphics = new Graphics() // 画笔
    if(actionType.value == 7) {
      brushGraphics.filters = new PixelateFilter([20,20])
    }
    drawContainer.addChild(brushGraphics); // 将画笔添加到图层
    brushGraphics.beginPath()
    brushGraphics.moveTo(x, y);
  })
  // 移动指针
  bgSprite.on('pointermove', (e: FederatedPointerEvent) => {
    const {x, y} = e.global
    if(isDraw) {
      brushGraphics.lineTo(x, y);
      if(actionType.value == 7) {
        brushGraphics.stroke({ color: 0xffffff, width: 30, alpha: 0.6 })
      } else {
        brushGraphics.stroke({ color: activeColor.value, width: 5 })
      }
    }
  })
  // 指针按钮释放
  bgSprite.on('pointerup', (_e) => {
    const {x, y} = _e.global
    isDraw = false
    brushGraphics.moveTo(x, y);
    brushGraphics.closePath()
  })
}

// 激活圆形
function activateCircle() {
  let circleGraphics = new Graphics() // 圆形
  let beginX = 0, beginY =0;
  let isDraw = false;
  bgSprite.on('pointerdown', async (e: FederatedPointerEvent) => {
    circleGraphics = new Graphics()
    drawContainer.addChild(circleGraphics); // 将圆形添加到图层
    isDraw = true;
    const {x, y} = e.global
    beginX = x, beginY = y;
    circleGraphics.beginPath()
    .circle(x, y, 5)
    .stroke({ color: activeColor.value, width: 5 })
  })
  bgSprite.on('pointermove', (e: FederatedPointerEvent) => {
    const {x, y} = e.global
    if(isDraw) {
      circleGraphics.clear()
      let scale = 1
      if(drawContainer.width > drawContainer.height){
        scale = (y- beginY)
      } else {
        scale = (x- beginX)
      }
      if(scale <0) scale = -scale
      circleGraphics.circle(beginX, beginY, scale)
      circleGraphics.stroke({ color: activeColor.value, width: 5 })
    }
  })
  bgSprite.on('pointerup', (_e: FederatedPointerEvent) => {
    circleGraphics.closePath()
    isDraw = false;
  })
}
// 激活矩形
function activateRect(rect:any = activeRect.value) {
  let rectGraphics = new Graphics() // 图形
  let beginX = 0, beginY =0;
  let isDraw = false;
  bgSprite.on('pointerdown', async (e: FederatedPointerEvent) => {
    rectGraphics = new Graphics()
    drawContainer.addChild(rectGraphics); // 将圆形添加到图层
    isDraw = true;
    const {x, y} = e.global
    beginX = x, beginY = y;
    rectGraphics.beginPath()
    rectGraphics['rect'](x, y, 5, 5)
    rectGraphics.stroke({ color: activeColor.value, width: 5 })
  })
  bgSprite.on('pointermove', (e: FederatedPointerEvent) => {
    const {x, y} = e.global
    if(isDraw) {
      rectGraphics.clear()
      let nX = (x-beginX)
      let nY = (y-beginY)
      if(nX<0&& nY<0){
        (rectGraphics as any)[rect](x, y, -nX, -nY)
      } else if(nX<0) {
        (rectGraphics as any)[rect](x, beginY, -nX, nY)
      } else if(nY<0) {
        (rectGraphics as any)[rect](beginX, y, nX, -nY)
      } else {
        (rectGraphics as any)[rect](beginX, beginY, nX, nY)
      }
      rectGraphics.stroke({ color: activeColor.value, width: 5 })
    }
  })
  bgSprite.on('pointerup', (_e: FederatedPointerEvent) => {
    rectGraphics.closePath()
    isDraw = false;
  })
}
// 激活文字输入框
function activateText() {
  bgSprite.on('pointerdown', (e: FederatedPointerEvent) => {
    if(showInput.value) return;
    inputText = new BitmapText({
     text: '',
     style: {
        //  fontFamily: '微软雅黑',
         fontSize: 40,
         fill: activeColor.value,
         align: 'left',
     }
    })
    inputText.anchor.x = 0.5
    const {x, y} = e.global
    const width = 400, height = 40;
    TextInput.value.style.color = activeColor.value.replace(/^0x{1}/,'#');
    TextInput.value.style.borderColor = activeColor.value.replace(/^0x{1}/,'#');
    TextInput.value.style.width = width+'px';
    TextInput.value.style.height = height+'px';
    TextInput.value.style.left = Number(x-(width/2))+'px';
    TextInput.value.style.top = Number(y-(height/2))+'px';
    showInput.value = true;
    setTimeout(() => {
      TextInput.value.focus();
    }, 100)
    inputText.position.set(x, Number(y-(height/2)));
    inputText.setSize(width, height);
  })
}

function onTextInputBlur() {
  showInput.value = false;
  if(word.value != '') {
    inputText.text = word.value
    drawContainer.addChild(inputText)
    word.value = ''
  }
  // actionType.value = 3
}
// 截图
function tailor(rect:any = activeRect.value) { 
  let beginX = 0, beginY =0;
  let isDraw = false;
  bgSprite.on('pointerdown', async (e: FederatedPointerEvent) => {
    mask = new Graphics()
    mask.label = 'mask'
    mask.beginPath()
    drawContainer.addChild(mask); // 将圆形添加到图层
    isDraw = true;
    const {x, y} = e.global
    beginX = x, beginY = y;
    (mask as any)[rect](x, y, 100, 100); // 创建一个100x100的矩形遮罩
    mask.stroke({ color: 0x000000, pixelLine: true })
  })
  bgSprite.on('pointermove', (e: FederatedPointerEvent) => {
    const {x, y} = e.global
    if(isDraw) {
      mask.clear()
      let nX = (x-beginX)
      let nY = (y-beginY)
      if(nX<0&& nY<0){
        (mask as any)[rect](x, y, -nX, -nY);
      } else if(nX<0) {
        (mask as any)[rect](x, beginY, -nX, nY);
      } else if(nY<0) {
        (mask as any)[rect](beginX, y, nX, -nY);
      } else {
        (mask as any)[rect](beginX, beginY, nX, nY);
      }
      mask.stroke({ color: 0x000000, pixelLine: true })
    }
  })
  bgSprite.on('pointerup', async (_e: FederatedPointerEvent) => {
    const {x, y} = _e.global
    isDraw = false;
    let nX = (x-beginX)
    let nY = (y-beginY)
    if(nX<0&& nY<0){
      (mask as any)[rect](x, y, -nX, -nY);
    } else if(nX<0) {
      (mask as any)[rect](x, beginY, -nX, nY);
    } else if(nY<0) {
      (mask as any)[rect](beginX, y, nX, -nY);
    } else {
      (mask as any)[rect](beginX, beginY, nX, nY);
    }
    mask.closePath()
    mask.fill(0x000000);
    drawContainer.mask = mask;
  })
}
function selectAction(type: number) {
  // clickAudio.value.play()
  if(type <3 || type == 7||type == 8) {
   bgSprite.off('pointerdown')
   bgSprite.off('pointermove')
   bgSprite.off('pointerup')
   actionType.value = type;
  }
  switch(type) {
    case 0: // 画笔
     bgSprite.cursor = `url(${brushSvg}) 10 26, auto`
      activateBrush();
    break;
    case 1: // 圆形
    bgSprite.cursor = 'crosshair'
    if(activeRect.value == 'circle') {
      activateCircle();
    } else {
      activateRect();
    }
    break;
    case 2: // 文本
    bgSprite.cursor = 'text'
    activateText();
    break;
    case 3: // 撤回
    handleRecall();
    break;
    case 4: // 重做
    handleReset();
    break;
    case 5: // 确认
    handleConfirm();
    break;
    case 6: // 关闭
    handleConfirm();
    break;
    case 7: // 马赛克
    bgSprite.cursor = `pointer`
    activateBrush();
    break;
    case 8: // 裁剪
    bgSprite.cursor = 'crosshair'
    tailor()
    break;
  }
}
function handleAddImage() {
  const input = document.createElement('input')
  input.type = 'file'
  input.accept ='.png,jpg,jpeg'
  input.click();
  input.onchange = ((event) => {
    const file = event.target?.files[0];
    const fileRender = new FileReader()
    fileRender.readAsDataURL(file)
    fileRender.onload = () => {
      imgUrl.value = fileRender.result as string
      renderBgSprite()
    }
  })
}
// 撤回
function handleRecall() {
  const children = Array.from(drawContainer.children).reverse()[0]
  if(children.label == 'mask') {
    mask.setSize(application.value.screen.width,application.value.screen.height)
    mask.rect(0,0, application.value.screen.width, application.value.screen.height)
    mask.fill(0x000000)
    drawContainer.mask = mask;
  }
  if (drawContainer.children.length > 1) {
    drawContainer.removeChild(drawContainer.children[drawContainer.children.length - 1]);
  }
}
// 重置
function handleReset() {
  mask.setSize(application.value.screen.width,application.value.screen.height)
  mask.rect(0,0, application.value.screen.width, application.value.screen.height)
  mask.fill(0x000000)
  bgSprite.mask = mask;
  const len = Array.from(drawContainer.children).length;
  for(let i = len; i>=1;i--){
    drawContainer.removeChild(drawContainer.children[i]);
  }
}
// 生成图片资源
async function handleConfirm() {
  if(mask.width != 0) {
    bgContainer.width = mask.width
    bgContainer.height = mask.height
  }
  await application.value.renderer.extract.download(bgContainer); // 会直接触发下载将图片保存到本地
  // const base64String = await  application.value.renderer.extract.base64(bgContainer);
}
onMounted(async () => {
  await nextTick();
  clickAudio.value = new Howl({
    src: [clickMp3], // 提供多个格式以适应不同浏览器
    autoplay: false,
    loop: false,
    volume: 0.5
  })
  const toolbarEl = document.querySelector('.toolbar')
  toolbarEl?.addEventListener('click', () => {
    clickAudio.value.play()
  })
  renderScene();
  window.addEventListener('resize', () => {
    if(!rootSceneRef.value) return;
    if(bgSprite) {
      bgSprite.width = rootSceneRef.value.clientWidth
      bgSprite.height = rootSceneRef.value.clientHeight
    }
  })
})
</script>

<style lang="scss" scoped>
.pe-box {
  position: relative;
  width: 100%;
  height: 100%;
  overflow: hidden;
  z-index: 1;
  font-family: '微软雅黑';
  #rootScene{
    z-index: 1;
    width: 100%;
    height: 100%;
    overflow: hidden;
    cursor: url('@/assets/cursor/brush.png') 10 26, auto;
  }
  .text-input {
    z-index: 3;
    position: absolute;
    top: 0;
    left: 0;
    width: 200px;
    height: 40px;
    border: 1px dashed #fff;
    background: rgba(0,0,0,0);
    // color: red;
    font-size: 20px;
    outline: none;
    text-align: center;
    cursor: text;
    // fontFamily: '微软雅黑';
  }
  .toolbar {
    z-index: 10;
    position: absolute;
    top: 0;
    right: 0;
    background: rgba(0,0,0,0.6);
    width: 60px;
    // max-height: 200px;
    border-radius: 0 0 0 3px;
    overflow: hidden;
    overflow-y: auto;
    ul {
      margin: 0;
      width: 100%;
      padding: 0;
      color: #ffffff;
      li {
        list-style: none;
        text-align: center;
        width: 100%;
        height: 20px;
        line-height: 20px;
        border-bottom: 1px solid #333;
        &:last-child {
          border: none;
        }
        cursor: pointer;
        .toolbar-icon {
          margin-right: 5px;
          font-size: 10px;
        }
        .select {
          width: 75%;
          height: 75%;
          border-radius: 3px;
          outline: none;
          color: #ffffff;
          background: transparent;
          option {
            background: #333;
          }
        }
      }
    }
  }
}
.activeColor {
  color: #2196F3;
  .select {
    color: #2196F3 !important;
    option {
      color: #ffffff !important;
    }
  }
}
</style>